﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq.Expressions;
using IDI.Studio.Core;

namespace IDI.Studio.Data
{
    /// <summary>
    /// Represents the base class for repositories.
    /// </summary>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    public abstract partial class Repository<TEntity> : IRepository<TEntity> where TEntity : Entity
    {
        private readonly IRepositoryContext context;

        #region Constructor

        /// <summary>
        /// Initializes a new instance of <c>Repository&lt;TObject&gt;</c> class.
        /// </summary>
        /// <param name="context">The repository context being used by the repository.</param>
        public Repository(IRepositoryContext context)
        {
            this.context = context;
        }

        #endregion

        #region IRepository<TObject> Members

        /// <summary>
        /// Gets the <see cref="IDI.Studio.Core.Repositories.IRepositoryContext"/> instance.
        /// </summary>
        public IRepositoryContext Context
        {
            get { return this.context; }
        }

        /// <summary>
        /// Adds an entity to the repository.
        /// </summary>
        /// <param name="entity">The entity to be added to the repository.</param>
        public void Add(TEntity entity)
        {
            this.DoAdd(entity);
        }

        /// <summary>
        /// Updates the entity in the current repository.
        /// </summary>
        /// <param name="entity">The entity to be updated.</param>
        public void Remove(TEntity entity)
        {
            this.DoRemove(entity);
        }

        /// <summary>
        /// Updates the entity in the current repository.
        /// </summary>
        /// <param name="entity">The entity to be updated.</param>
        public void Update(TEntity entity)
        {
            this.DoUpdate(entity);
        }

        /// <summary>
        /// Checkes whether the entity, which matches the given specification, exists in the repository.
        /// </summary>
        /// <param name="specification">The specification with which the entity should match.</param>
        /// <returns>True if the entity exists, otherwise false.</returns>
        public bool Exists(ISpecification<TEntity> specification)
        {
            return this.DoExists(specification);
        }

        /// <summary>
        /// Gets the entity instance from repository by a given key.
        /// </summary>
        /// <param name="key">The key of the entity.</param>
        /// <returns>The instance of the entity.</returns>
        public TEntity GetByKey(Guid key)
        {
            return this.DoGetByKey(key);
        }

        /// <summary>
        /// Finds a single entity that matches the given specification.
        /// </summary>
        /// <param name="specification">The specification with which the entity should match.</param>
        /// <returns>The instance of the entity.</returns>
        public TEntity Find(ISpecification<TEntity> specification)
        {
            return this.DoFind(specification);
        }

        /// <summary>
        /// Finds a single entity from the repository.
        /// </summary>
        /// <param name="specification">The specification with which the entity should match.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        public TEntity Find(ISpecification<TEntity> specification, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return this.DoFind(specification, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <returns>All the entities got from the repository.</returns>
        public IEnumerable<TEntity> FindAll()
        {
            return this.DoFindAll();
        }

        /// <summary>
        /// Finds all the entities from repository, sorting by using the provided sort predicate
        /// and the specified sort order.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <returns>All the entities got from the repository, with the entities being sorted by
        /// using the provided sort predicate and the sort order.</returns>
        public IEnumerable<TEntity> FindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder)
        {
            return this.DoFindAll(sortPredicate, sortOrder);
        }

        /// <summary>
        /// Finds all the entities from repository with paging enabled, sorting by using the provided sort predicate
        /// and the specified sort order.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The page number.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <returns>All the entities got from the repository, with the entities being sorted by
        /// using the provided sort predicate and the sort order.</returns>
        public PagedResult<TEntity> FindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize)
        {
            return this.DoFindAll(sortPredicate, sortOrder, pageNumber, pageSize);
        }

        /// <summary>
        /// Finds all the entities that match the given specification.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <returns>All the entities that match the given specification.</returns>
        public IEnumerable<TEntity> FindAll(ISpecification<TEntity> specification)
        {
            return this.DoFindAll(specification);
        }

        /// <summary>
        /// Finds all the entities that match the given specification, and sorts the entities
        /// by using the provided sort predicate and the specified sort order.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <returns>All the entities that match the given specification and were sorted by using the given sort predicate and the sort order.</returns>
        public IEnumerable<TEntity> FindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder)
        {
            return this.DoFindAll(specification, sortPredicate, sortOrder);
        }

        /// <summary>
        /// Finds all the entities that match the given specification with paging enabled, and sorts the entities
        /// by using the provided sort predicate and the specified sort order.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The number of entities per page.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <returns>All the entities that match the given specification and were sorted by using the given sort predicate and the sort order.</returns>
        public PagedResult<TEntity> FindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize)
        {
            return this.DoFindAll(specification, sortPredicate, sortOrder, pageNumber, pageSize);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        public IEnumerable<TEntity> FindAll(params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return this.DoFindAll(eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        public IEnumerable<TEntity> FindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return this.DoFindAll(sortPredicate, sortOrder, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The page number.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        public PagedResult<TEntity> FindAll(Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return this.DoFindAll(sortPredicate, sortOrder, pageNumber, pageSize, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        public IEnumerable<TEntity> FindAll(ISpecification<TEntity> specification, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return this.DoFindAll(specification, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        public IEnumerable<TEntity> FindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return this.DoFindAll(specification, sortPredicate, sortOrder, eagerLoadingProperties);
        }

        /// <summary>
        /// Finds all the entities from repository.
        /// </summary>
        /// <param name="specification">The specification with which the entities should match.</param>
        /// <param name="sortPredicate">The sort predicate which is used for sorting.</param>
        /// <param name="sortOrder">The <see cref="SortOrder"/> enumeration which specifies the sort order.</param>
        /// <param name="pageNumber">The page number.</param>
        /// <param name="pageSize">The number of entities per page.</param>
        /// <param name="eagerLoadingProperties">The properties for the aggregated entities that need to be loaded.</param>
        /// <returns>The entity.</returns>
        public PagedResult<TEntity> FindAll(ISpecification<TEntity> specification, Expression<Func<TEntity, dynamic>> sortPredicate, SortOrder sortOrder, int pageNumber, int pageSize, params Expression<Func<TEntity, dynamic>>[] eagerLoadingProperties)
        {
            return this.DoFindAll(specification, sortPredicate, sortOrder, pageNumber, pageSize, eagerLoadingProperties);
        }

        #endregion
    }
}
